<!doctype html><html class="use-motion" lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1"><meta http-equiv="Cache-Control" content="no-transform"><meta http-equiv="Cache-Control" content="no-siteapp"><meta name="google-site-verification" content="0axhmy0-lA3xPbR8wPgyWlMZjwWSCfagKukdyeCQ7LU"><link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css"><link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css"><link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css"><meta name="keywords" content="Conntrack,NAT,ovs-dpdk,OVS,OpenvSwitch,SDN,DPDK,Network"><link rel="alternate" href="/atom.xml" title="赵占旭的博客" type="application/atom+xml"><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0"><meta name="description" content="这里只是捡重点的说，需要原版去看ovs-ofctl(8)。"><meta name="keywords" content="Conntrack,NAT,ovs-dpdk,OVS,OpenvSwitch,SDN,DPDK,Network"><meta property="og:type" content="article"><meta property="og:title" content="OpenvSwitch Conntrack &amp; NAT"><meta property="og:url" content="http://zhaozhanxu.com/2017/02/10/SDN/OVS/2017-02-10-conntrack/index.html"><meta property="og:site_name" content="赵占旭的博客"><meta property="og:description" content="这里只是捡重点的说，需要原版去看ovs-ofctl(8)。"><meta property="og:locale" content="en"><meta property="og:updated_time" content="2017-12-11T14:31:18.941Z"><meta name="twitter:card" content="summary"><meta name="twitter:title" content="OpenvSwitch Conntrack &amp; NAT"><meta name="twitter:description" content="这里只是捡重点的说，需要原版去看ovs-ofctl(8)。"><script type="text/javascript" id="hexo.configurations">var NexT=window.NexT||{},CONFIG={root:"/",sidebar:{position:"left",display:"post",offset:342,offset_float:0},fancybox:!0,motion:!0}</script><link rel="canonical" href="http://zhaozhanxu.com/2017/02/10/SDN/OVS/2017-02-10-conntrack/"><title> OpenvSwitch Conntrack & NAT | 赵占旭的博客</title></head><body itemscope itemtype="http://schema.org/WebPage" lang="en"><script>!function(e,a,t,c,n,g,o){e.GoogleAnalyticsObject="ga",e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,g=a.createElement("script"),o=a.getElementsByTagName("script")[0],g.async=1,g.src="//www.google-analytics.com/analytics.js",o.parentNode.insertBefore(g,o)}(window,document),ga("create","3a235f2280210fdd34cebc8c3647e5b8","auto"),ga("send","pageview")</script><script type="text/javascript">var _hmt=_hmt||[];!function(){var e=document.createElement("script");e.src="//hm.baidu.com/hm.js?UA-90327054-1";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><script type="text/javascript">!function(){var e=document.createElement("script");e.src="//tajs.qq.com/stats?sId=60806179";var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t)}()</script><div class="container one-collumn sidebar-position-left page-post-detail"><div class="headband"></div><header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader" style="background-image:url(/images/public/home_bg.jpg)"><div class="header-cover"><div class="header-inner"><a class="site-home" href="/">赵占旭的博客</a><div class="site-meta"><div class="custom-logo-site-title"><div href="/" class="brand"><span class="logo-line-before"><i></i></span> <span class="site-title">赵占旭的博客</span><span class="logo-line-after"><i></i></span></div></div><h1 class="site-subtitle" itemprop="description">一花一世界 一叶一菩提</h1></div><div class="site-nav-toggle"> <button><span class="btn-bar"></span><span class="btn-bar"></span><span class="btn-bar"></span></button></div><nav class="site-nav"><ul id="menu" class="menu"><li class="menu-item menu-item-categories"><a href="/categories" rel="section"><i class="menu-item-icon fa fa-fw fa-th"></i><br> Categories</a></li><li class="menu-item menu-item-tags"><a href="/tags" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i><br> Tags</a></li><li class="menu-item menu-item-archives"><a href="/archives" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i><br> Archives</a></li><li class="menu-item menu-item-about"><a href="/about" rel="section"><i class="menu-item-icon fa fa-fw fa-user"></i><br> About</a></li><li class="menu-item menu-item-search"><a href="javascript:" ; class="popup-trigger"><i class="menu-item-icon fa fa-search fa-fw"></i><br> Search</a></li></ul><div class="site-search"><div class="popup local-search-popup"><div class="local-search-header clearfix"><span class="search-icon"><i class="fa fa-search"></i></span><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span><div class="local-search-input-wrapper"> <input autocapitalize="off" autocomplete="off" autocorrect="off" placeholder="search.placeholder" spellcheck="false" type="text" id="local-search-input"></div></div><div id="local-search-result"></div></div></div></nav></div><div class="header-post"><div class="post-header"><div class="tags"> <a href="/tags/OVS/" rel="tag" title="OVS">OVS</a> <a href="/tags/Network/" rel="tag" title="Network">Network</a> <a href="/tags/SDN/" rel="tag" title="SDN">SDN</a></div><h1>OpenvSwitch Conntrack & NAT</h1><h2 class="subtitle">OVS的conntrack和NAT，主要是介绍DPDK情况下的NAT补丁</h2><div class="post-time"> <span class="post-meta-item-text">Posted on</span> <time itemprop="dateCreated" datetime="2017-02-10T00:00:00+08:00" content="2017-02-10" title="2017-02-10 00:00:00">2017-02-10</time></div></div></div></div></header><main id="main" class="main"><div class="main-inner"><div class="content-wrap"><div id="content" class="content"><div id="posts" class="posts-expand"><article class="post post-type-normal" itemscope itemtype="http://schema.org/Article"><link itemprop="mainEntityOfPage" href="http://zhaozhanxu.com/2017/02/10/SDN/OVS/2017-02-10-conntrack/"><span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="name" content="赵占旭"><meta itemprop="description" content=""><meta itemprop="image" content="/images/public/me.jpg"></span><span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="赵占旭的博客"> <span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject"><img style="display:none" itemprop="url image" alt="赵占旭的博客" src=""></span></span><div class="post-inner"><header class="post-header"><h2 class="post-title" itemprop="name headline"> OpenvSwitch Conntrack & NAT</h2><div class="post-meta"><span class="post-time"><span class="post-meta-item-icon"><i class="fa fa-calendar-o"></i></span> <span class="post-meta-item-text">Posted on</span> <time title="Post created" itemprop="dateCreated datePublished" datetime="2017-02-10T00:00:00+08:00">2017-02-10</time></span></div></header><div class="post-info"><div class="post-categories"> <a class="color" href="/categories/OVS/" rel="tag">OVS</a></div><div class="post-tags"> <a class="color" href="/tags/OVS/" rel="tag">OVS</a> <a class="color" href="/tags/Network/" rel="tag">Network</a> <a class="color" href="/tags/SDN/" rel="tag">SDN</a></div><div class="clearfix"></div></div><div class="post-body" itemprop="articleBody"><p>这里只是捡重点的说，需要原版去看<a href="http://openvswitch.org/support/dist-docs/ovs-ofctl.8.txt" target="_blank" rel="noopener">ovs-ofctl(8)</a>。</p><a id="more"></a><p><strong>这里只关注OpenvSwitch的DPDK相关的conntrack和NAT。</strong></p><h1 id="conntrack匹配项说明"><a href="#conntrack匹配项说明" class="headerlink" title="conntrack匹配项说明"></a>conntrack匹配项说明</h1><hr><p>match项表示为ct_state=flags/mask或者ct_state=[+flag…][-flag…]</p><p>单独的报文处于两个状态：untracked和tracked。前者是指报文还没有进入conntrack中，用-trk表示，这时候一般是用来执行ct的action的，然后就会变成后者，即+trk。</p><p>连接有两个状态：uncommitted和committed。连接默认是uncommitted。要确认连接信息，比如连接是否established状态，连接状态必须是committed。当带有commit参数的ct_action处理一个报文时，连接会变成committed状态知道连接终止。连接committed状态持续时间超过报文的持续时间。</p><p>一般的用法是报文untracked的时候，就会调用ct(commit)的actions，这样报文也进入conntrack，状态也变成了commited，后续conntrack就可以继续更新状态。</p><p>以下是flags的描述：</p><ul><li>0x01：new 新建连接的开始，这个表示一般存在于uncommitted状态的连接中。</li><li>0x02：est 这是已经存在，并且准备好的连接，存在于committed连接。</li><li>0x04：rel 这是和一个存在连接相关的连接。比如ICMP 目的不可达消息或者FTP数据连接。存在于committed连接。</li><li>0x08：rpl 这个流是反方向，意味着没有初始化连接。存在于committed连接。</li><li>0x10：inv 无效状态，意味着连接跟踪不能识别连接。这个标志包含了一个连接跟踪可能遇到的任何问题，例如：L3/L4协议处理程序没有加载或者不可用。在Linux内核datapath，意味着nf_conntrack_ipv4或者nf_conntrack_ipv6模块没有加载。L3/L4协议处理程序确定报文分组格式不对。报文协议的长度不对。</li><li>0x20：trk 报文状态是tracked，意味着他先前已经经历了连接跟踪。如果该标志没有设置，其他的标志也不会被设置。如果这个标志设置了，报文状态为tracked，其他的标志才会被设置。这个字段是Open vSwitch2.5才引入的。</li></ul><p>以下的字段于连接跟踪有关，并且只用于跟踪的数据报文。ct_action也用于填充和修改这些字段。</p><ul><li>ct_zone=zone 匹配给出的16位连接区域。这代表了CT执行的最近的连接跟中上下文。每个zone是一个独立的连接跟踪上下文，所以你想在多个上下文中跟踪一个报文，你必须使用ct_action多次。</li><li>ct_mark=value[/mask] 匹配给出的32位连接mark值。这表示元数据和报文所属的连接有联系。</li><li>ct_label=value[/mask] 匹配128位的连接label值。这表示元数据和报文所属的连接有联系。</li></ul><h1 id="conntrack-actions执行"><a href="#conntrack-actions执行" class="headerlink" title="conntrack actions执行"></a>conntrack actions执行</h1><hr><p>actions项表示为ct([argument][,argument…])</p><p>通过conntrack发送报文。依赖ct_state，支持以下参数：</p><ul><li>commit 提交连接到connection tracking 模块。连接信息的存储时间远高于报文的生存时间。一些ct_state只为commited连接。</li><li>table=number 将流程处理一分为二，前面都是按照untracked的报文处理。后者是将报文发送到connection tracker模块，并且回注到OpenFlow流程，并且在num的table里面处理，设置ct_state和其他ct匹配的字段。如果table没有指定，提交到connection tracker的报文不会回注到OpenFlow。强烈建议指定比当前table靠后的table，以防止循环。</li><li>zone=value或者zone=src[start…end] 一个16位上下文id，将连接隔离到不同的domains，不同的zones允许重叠的网络地址。如果zone没有指定，默认使用zone0。zone可以用一个16进制的数字指定，也可以用NXM的src字段指定。start和end指定一个16位的区间。报文回注到table指定的处理流程时，这个值会拷贝到ct_zone的match字段。</li><li>exec([action][,action]) 在conntrack上下文执行actions。这是一个和指定flow相同格式的actions。actions修改ct_mark或者ct_label字段，这些也可以放在exec里面。例如：<ul><li>set_field:value-&gt;ct_mark 存储一个32位的元数据到连接。如果连接是commited，当报文发送到table指定的conntrack时，这个连接的报文查找将填充ct_mark流表字段。</li><li>set_field:value-&gt;ct_label 存储一个128位的元数据到连接。如果连接的状态是commited，当报文发送到table指定的conntrack时，连接的报文将填充ct_label刘字段。</li><li>commit参数需要指定使用exec(…)</li></ul></li><li>alg=alg 指定应用层网关alg到指定的连接状态的conntrack。支持的类型有：<ul><li>ftp 查找协商的FTP数据连接。如果后面相关的FTP数据连接到达时，报文通过ct时，ct action在ct_state字段设置rel标志。</li><li>tftp 查找协商的TFTP数据连接。如果后面相关的TFTP数据连接到达时，报文通过ct时，ct action在ct_state字段设置rel标志。</li></ul></li><li>nat[((src|dst)=addr1[-addr2][:port1[-port2]][,flags])] 为跟踪的连接进行地址和端口转换。只有commit的连接才能进行NAT操作。<ul><li>addr1[-addr2] 翻译的地址</li><li>port1[-port2] 翻译的端口</li><li>flags包含以下几种<ul><li>random 范围的随机选取，与hash互斥</li><li>hash 进行hash选取，与random互斥</li><li>persistent 系统启动的时候就提供了固定的映射</li></ul></li></ul></li><li>ct_clear 清空</li></ul><p>ct action用作有状态的防火墙结构，提交的流量匹配ct_state允许establish连接，但是拒绝新连接。</p><h1 id="ACL示例"><a href="#ACL示例" class="headerlink" title="ACL示例"></a>ACL示例</h1><hr><p>以下流表提供了一个例子，实现了简单的ACL，允许端口1到端口2的连接，只允许端口2到端口1的establish的连接：</p><h2 id="应用层流表"><a href="#应用层流表" class="headerlink" title="应用层流表"></a>应用层流表</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">//优先级最低，丢包</span><br><span class="line">table=0,priority=1,action=drop</span><br><span class="line">//arp报文不管</span><br><span class="line">table=0,priority=10,arp,action=normal</span><br><span class="line">//没有加入conntrack的报文，执行ct加入conntrack，并且发送到table 1</span><br><span class="line">table=0,priority=100,ip,ct_state=-trk,action=ct(table=1)</span><br><span class="line">//1口的报文，并且是新连接的则，执行ct的commit，创建连接，并且报文发给2口</span><br><span class="line">table=1,in_port=1,ip,ct_state=+trk+new,action=ct(commit),2</span><br><span class="line">//1口的报文，连接已经建立完成，establish状态，直接发给2口</span><br><span class="line">table=1,in_port=1,ip,ct_state=+trk+est,action=2</span><br><span class="line">//2口到1口的报文，状态是new的，丢弃</span><br><span class="line">table=1,in_port=2,ip,ct_state=+trk+new,action=drop</span><br><span class="line">//2口到1口的报文，状态是establish，直接发给1口</span><br><span class="line">table=1,in_port=2,ip,ct_state=+trk+est,action=1</span><br></pre></td></tr></table></figure><p>如果ct操作分片的ip报文，首先隐式的重组报文，并且发送到conntrack，输出时再重新分片。重组发生在zone的上下文，意味着发送到不同zone的分片将不能重组。<br>处理分片报文时暂停，当最后一个分片收到时，重组然后继续处理。因为报文排序不由IP协议保证，不能确定哪些IP分片导致充足。因此，我们强烈建议多流不应该执行ct来重组相同IP的分片。</p><h2 id="datapath流表"><a href="#datapath流表" class="headerlink" title="datapath流表"></a>datapath流表</h2><p>datapath的流表结果，端口1向2发起连接<br></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">//一直持续，主要是将报文纳入conntrack中，然后actions的ct表示根据报文更新conntrack状态，第一次更新为new</span><br><span class="line">ct_state(-trk),recirc_id(0),in_port(1),eth_type(0x0800),ipv4(frag=no), packets:331672, bytes:500255400, used:0.000s, flags:SP., actions:ct,recirc(0x8)</span><br><span class="line">//持续时间很短，只是新建的时候存在一会，发给端口2</span><br><span class="line">ct_state(+new+trk),recirc_id(0x8),in_port(1),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:74, used:1.763s, flags:S, actions:ct(commit),2</span><br><span class="line">//一直持续，主要是符合条件的报文发给端口2</span><br><span class="line">ct_state(-new+est+trk),recirc_id(0x8),in_port(1),eth_type(0x0800),ipv4(frag=no), packets:331650, bytes:500224980, used:0.000s, flags:P., actions:2</span><br><span class="line">//一直持续，将反方向的报文纳入conntrack中，然后根据报文更新conntrack状态，这次更新为est</span><br><span class="line">ct_state(-trk),recirc_id(0),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:163789, bytes:10846030, used:0.000s, flags:SP., actions:ct,recirc(0x9)</span><br><span class="line">//一直持续，主要是符合条件的报文发给端口1</span><br><span class="line">ct_state(-new+est+trk),recirc_id(0x9),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:163789, bytes:10846030, used:0.000s, flags:SP., actions:1</span><br></pre></td></tr></table></figure><p></p><p>端口2向1发起连接<br></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ct_state(+new+trk),recirc_id(0x6),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:74, used:1.353s, flags:S, actions:drop</span><br><span class="line">ct_state(-trk),recirc_id(0),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:74, used:1.353s, flags:S, actions:ct,recirc(0x6)</span><br></pre></td></tr></table></figure><p></p><h1 id="NAT示例"><a href="#NAT示例" class="headerlink" title="NAT示例"></a>NAT示例</h1><hr><p>以下流表提供了一个例子，实现了简单的NAT，允许端口1(192.168.1.10/24)的报文SNAT到10.0.0.1/24和端口5000-50000：</p><h2 id="应用层流表-1"><a href="#应用层流表-1" class="headerlink" title="应用层流表"></a>应用层流表</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">//优先级最低，不管</span><br><span class="line">table=0,priority=1,action=normal</span><br><span class="line">//没有加入conntrack的报文，执行ct加入conntrack，执行NAT，并且发送到table 1</span><br><span class="line">table=0,priority=10,ip,ct_state=-trk,action=ct(nat,table=1)</span><br><span class="line">//1口的报文，并且是新连接的则，执行ct的commit，创建连接，执行NAT规则，并且报文发给2口</span><br><span class="line">table=1,in_port=1,ip,ct_state=+trk+new,action=ct(nat(src=10.0.0.1-10.0.0.255:5000-50000),commit),2</span><br><span class="line">//1口的报文，连接已经建立完成，establish状态，直接发给2口</span><br><span class="line">table=1,in_port=1,ip,ct_state=+trk+est,action=2</span><br><span class="line">//2口到1口的报文，状态是establish，直接发给1口</span><br><span class="line">table=1,in_port=2,ip,ct_state=+trk+est,action=1</span><br></pre></td></tr></table></figure><h2 id="datapath流表-1"><a href="#datapath流表-1" class="headerlink" title="datapath流表"></a>datapath流表</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">//一直持续，主要是将报文纳入conntrack中，然后actions的ct表示根据报文更新conntrack状态，第一次更新为new，执行NAT操作</span><br><span class="line">ct_state(-trk),recirc_id(0),in_port(1),eth_type(0x0800),ipv4(frag=no), packets:325157, bytes:489925194, used:0.000s, flags:SP., actions:ct(nat),recirc(0x7)</span><br><span class="line">//持续时间很短，只是新建的时候存在一会，创建NAT规则，发给端口2</span><br><span class="line">ct_state(+new+trk),recirc_id(0x7),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:1, bytes:74, used:1.451s, flags:S, actions:ct(commit,nat(src=10.0.0.1-10.0.0.255:5000-50000)),2</span><br><span class="line">//一直持续，主要是符合条件的报文发给端口2</span><br><span class="line">ct_state(-new+est+trk),recirc_id(0x7),in_port(3),eth_type(0x0800),ipv4(frag=no), packets:325155, bytes:489925054, used:0.000s, flags:P., actions:2</span><br><span class="line">//一直持续，将反方向的报文纳入conntrack中，然后根据报文更新conntrack状态，这次更新为est，执行NAT操作</span><br><span class="line">ct_state(-trk),recirc_id(0),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:162817, bytes:10752486, used:0.000s, flags:SP., actions:ct(nat),recirc(0x8)</span><br><span class="line">//一直持续，主要是符合条件的报文发给端口1</span><br><span class="line">ct_state(-new+est+trk),recirc_id(0x8),in_port(2),eth_type(0x0800),ipv4(frag=no), packets:162815, bytes:10752354, used:0.000s, flags:SP., actions:1</span><br></pre></td></tr></table></figure><h1 id="datapath代码分析"><a href="#datapath代码分析" class="headerlink" title="datapath代码分析"></a>datapath代码分析</h1><hr><p><strong>这里主要关注action，未关注match部分。</strong></p><p><code>pmd_thread_main--&gt;dp_netdev_process_rxq_port--&gt;dp_netdev_input--&gt;dp_netdev_input__</code>主要有match函数<code>emc_processing</code>和action操作函数<code>packet_batch_execute</code>。</p><ul><li><code>emc_processing</code>主要将不匹配的报文去大表里面查，匹配的则放到流表对应的队列中。</li><li><code>packet_batch_execute--&gt;dp_execute_cb</code>主要是根据action去操作队列里面的报文。</li></ul><p>现在主要关注conntrack部分的代码</p><p><code>dp_execute_cb</code></p><ul><li><code>OVS_ACTION_ATTR_OUTPUT</code>，调用<code>dp_netdev_lookup_port</code>查找端口，然后调用<code>netdev_send</code>进行报文发送。</li><li><code>OVS_ACTION_ATTR_TUNNEL_PUSH</code>，调用<code>push_tnl_action</code>进行tunnel封装，然后调用<code>dp_netdev_recirculate--&gt;dp_netdev_input__</code>重新查表操作。</li><li><code>OVS_ACTION_ATTR_TUNNEL_POP</code>，调用<code>netdev_pop_header</code>解封装，然后调用<code>dp_netdev_recirculate--&gt;dp_netdev_input__</code>重新查表操作。</li><li><code>OVS_ACTION_ATTR_RECIRC</code>，给报文赋值recirc，然后调用<code>dp_netdev_recirculate--&gt;dp_netdev_input__</code>重新查表操作。</li><li><code>OVS_ACTION_ATTR_CT</code>，解析CT内的一些<code>sub_type</code>，然后调用<code>conntrack_execute</code>操作跟踪的连接。<strong>这里需要注意的是<code>OVS_CT_ATTR_NAT</code>是不支持的</strong></li></ul><p><code>conntrack_execute</code></p><ul><li>每个报文调用<code>conn_key_extract</code>解析L2、3、4协议头，计算出src hash和 dst hash。然后加入<code>bucket_list</code>。</li><li>循环<code>bucket_list</code>以及bucket下的maps，调用<code>conn_key_lookup</code>找到连接，调用<code>process_one</code>更新连接状态。</li><li>根据情况<code>set_mark</code>或者<code>set_lable</code>。</li></ul><p><code>process_one</code></p><ul><li>首先判断有没有连接，如果有，则调用状态更新函数<code>conn_update</code>，主要是根据不同的协议调用不同的更新函数。</li><li>如说是状态<code>CT_UPDATE_NEW</code>，则表示是新的连接，需要删除旧的，重新建连接。</li><li>如果没有连接，调用<code>conn_not_found</code>创建连接。</li></ul><h1 id="NAT补丁"><a href="#NAT补丁" class="headerlink" title="NAT补丁"></a>NAT补丁</h1><hr><p>patch文件源自<a href="http://patchwork.ozlabs.org/patch/725530/" target="_blank" rel="noopener">这里</a>，后续可能还会更新，现在这里感谢作者的付出和共享。</p><p>前面提到的<code>dp_execute_cb</code>函数中没有支持<code>OVS_CT_ATTR_NAT</code>，所以这次的补丁我们就从这里看起。</p><p>patch之前的部分代码，NAT是没有支持的</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">dp_execute_cb</span><span class="params">(<span class="keyword">void</span> *aux_, struct dp_packet_batch *packets_,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> struct nlattr *a, <span class="keyword">bool</span> may_steal)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">switch</span> ((<span class="keyword">enum</span> ovs_action_attr)type) &#123;</span><br><span class="line">        <span class="keyword">case</span> OVS_ACTION_ATTR_CT: </span><br><span class="line">            NL_ATTR_FOR_EACH_UNSAFE (b, left, nl_attr_get(a),</span><br><span class="line">                        nl_attr_get_size(a)) &#123;</span><br><span class="line">                <span class="keyword">enum</span> ovs_ct_attr sub_type = nl_attr_type(b);</span><br><span class="line">                <span class="keyword">switch</span>(sub_type) &#123;</span><br><span class="line">                    <span class="comment">//NAT此处是不支持的</span></span><br><span class="line">                    <span class="keyword">case</span> OVS_CT_ATTR_NAT:</span><br><span class="line">                    <span class="keyword">case</span> OVS_CT_ATTR_UNSPEC:</span><br><span class="line">                    <span class="keyword">case</span> __OVS_CT_ATTR_MAX:</span><br><span class="line">                        OVS_NOT_REACHED();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            conntrack_execute(&amp;dp-&gt;conntrack, packets_, aux-&gt;flow-&gt;dl_type, commit,</span><br><span class="line">                        zone, setmark, setlabel, helper);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>patch之后的，NAT部分已经有了实现，主要是拿到一些NAT的配置信息，实现依然是使用的现有的接口<code>conntrack_execute</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">void</span></span><br><span class="line">dp_execute_cb(<span class="keyword">void</span> *aux_, struct dp_packet_batch *packets_,</span><br><span class="line">            <span class="keyword">const</span> struct nlattr *a, <span class="keyword">bool</span> may_steal)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">switch</span> ((<span class="keyword">enum</span> ovs_action_attr)type) &#123;</span><br><span class="line">        <span class="keyword">case</span> OVS_ACTION_ATTR_CT: </span><br><span class="line">            NL_ATTR_FOR_EACH_UNSAFE (b, left, nl_attr_get(a),</span><br><span class="line">                        nl_attr_get_size(a)) &#123;</span><br><span class="line">                <span class="keyword">enum</span> ovs_ct_attr sub_type = nl_attr_type(b);</span><br><span class="line">                <span class="keyword">switch</span>(sub_type) &#123;</span><br><span class="line">                    <span class="keyword">case</span> OVS_CT_ATTR_NAT: </span><br><span class="line">                        <span class="keyword">const</span> <span class="class"><span class="keyword">struct</span> <span class="title">nlattr</span> *<span class="title">b_nest</span>;</span></span><br><span class="line">                        <span class="keyword">unsigned</span> <span class="keyword">int</span> left_nest;</span><br><span class="line">                        <span class="keyword">bool</span> ip_min_specified = <span class="literal">false</span>;</span><br><span class="line">                        <span class="keyword">bool</span> proto_num_min_specified = <span class="literal">false</span>;</span><br><span class="line">                        <span class="keyword">bool</span> ip_max_specified = <span class="literal">false</span>;</span><br><span class="line">                        <span class="keyword">bool</span> proto_num_max_specified = <span class="literal">false</span>;</span><br><span class="line">                        <span class="built_in">memset</span>(&amp;nat_action_info, <span class="number">0</span>, <span class="keyword">sizeof</span> nat_action_info);</span><br><span class="line">                        nat_action_info_ref = &amp;nat_action_info;</span><br><span class="line"></span><br><span class="line">                        NL_NESTED_FOR_EACH_UNSAFE (b_nest, left_nest, b) &#123;</span><br><span class="line">                            <span class="keyword">enum</span> ovs_nat_attr sub_type_nest = nl_attr_type(b_nest);</span><br><span class="line"></span><br><span class="line">                            <span class="keyword">switch</span>(sub_type_nest) &#123;</span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_SRC:</span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_DST:</span><br><span class="line">                                    nat_config = <span class="literal">true</span>;</span><br><span class="line">                                    nat_action_info.nat_action |=</span><br><span class="line">                                        ((sub_type_nest == OVS_NAT_ATTR_SRC)</span><br><span class="line">                                         ? NAT_ACTION_SRC : NAT_ACTION_DST);</span><br><span class="line">                                    <span class="keyword">break</span>;</span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_IP_MIN:</span><br><span class="line">                                    <span class="built_in">memcpy</span>(&amp;nat_action_info.min_addr,</span><br><span class="line">                                                (<span class="keyword">char</span> *) b_nest + NLA_HDRLEN, b_nest-&gt;nla_len);</span><br><span class="line">                                    ip_min_specified = <span class="literal">true</span>;</span><br><span class="line">                                    <span class="keyword">break</span>;</span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_IP_MAX:</span><br><span class="line">                                    <span class="built_in">memcpy</span>(&amp;nat_action_info.max_addr,</span><br><span class="line">                                                (<span class="keyword">char</span> *) b_nest + NLA_HDRLEN, b_nest-&gt;nla_len);</span><br><span class="line">                                    ip_max_specified = <span class="literal">true</span>;</span><br><span class="line">                                    <span class="keyword">break</span>;</span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_PROTO_MIN:</span><br><span class="line">                                    nat_action_info.min_port = nl_attr_get_u16(b_nest);</span><br><span class="line">                                    proto_num_min_specified = <span class="literal">true</span>;</span><br><span class="line">                                    <span class="keyword">break</span>;</span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_PROTO_MAX:</span><br><span class="line">                                    nat_action_info.max_port = nl_attr_get_u16(b_nest);</span><br><span class="line">                                    proto_num_max_specified = <span class="literal">true</span>;</span><br><span class="line">                                    <span class="keyword">break</span>;</span><br><span class="line">                                <span class="comment">//以上是NAT的ip和端口的配置，这里比较关键的是映射机制没有设置！！！</span></span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_PERSISTENT:</span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_PROTO_HASH:</span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_PROTO_RANDOM:</span><br><span class="line">                                    <span class="keyword">break</span>;</span><br><span class="line">                                <span class="keyword">case</span> OVS_NAT_ATTR_UNSPEC:</span><br><span class="line">                                <span class="keyword">case</span> __OVS_NAT_ATTR_MAX:</span><br><span class="line">                                    OVS_NOT_REACHED();</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line"></span><br><span class="line">                        <span class="keyword">if</span> (ip_min_specified &amp;&amp; !ip_max_specified) &#123;</span><br><span class="line">                            <span class="built_in">memcpy</span>(&amp;nat_action_info.max_addr,</span><br><span class="line">                                        &amp;nat_action_info.min_addr,</span><br><span class="line">                                        <span class="keyword">sizeof</span>(nat_action_info.max_addr));</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (proto_num_min_specified &amp;&amp; !proto_num_max_specified) &#123;</span><br><span class="line">                            nat_action_info.max_port = nat_action_info.min_port;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">if</span> (proto_num_min_specified || proto_num_max_specified) &#123;</span><br><span class="line">                            <span class="keyword">if</span> (nat_action_info.nat_action &amp; NAT_ACTION_SRC) &#123;</span><br><span class="line">                                nat_action_info.nat_action |= NAT_ACTION_SRC_PORT;</span><br><span class="line">                            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nat_action_info.nat_action &amp; NAT_ACTION_DST) &#123;</span><br><span class="line">                                nat_action_info.nat_action |= NAT_ACTION_DST_PORT;</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                        <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line">                    <span class="keyword">case</span> OVS_CT_ATTR_UNSPEC:</span><br><span class="line">                    <span class="keyword">case</span> __OVS_CT_ATTR_MAX:</span><br><span class="line">                        OVS_NOT_REACHED();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (nat_config &amp;&amp; !commit) &#123;</span><br><span class="line">                VLOG_WARN(<span class="string">"NAT specified without commit."</span>);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//这里可以看到NAT的配置信息是传递到这个接口了</span></span><br><span class="line">            conntrack_execute(&amp;dp-&gt;conntrack, packets_, aux-&gt;flow-&gt;dl_type, commit,</span><br><span class="line">                        zone, setmark, setlabel, helper,</span><br><span class="line">                        nat_action_info_ref);</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>conntrack_execute</code>这里不列出来了，因为这块是代码的调整，后续可以细看一下为什么调整，主要就是将一些代码调整到函数<code>process_one</code>，而NAT的配置参数继续传递到了该函数，所以需要看一下。</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">process_one</span><span class="params">(struct conntrack *ct, struct dp_packet *pkt,</span></span></span><br><span class="line"><span class="function"><span class="params">            struct conn_lookup_ctx *ctx, <span class="keyword">uint16_t</span> zone,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">bool</span> commit, <span class="keyword">long</span> <span class="keyword">long</span> now, <span class="keyword">const</span> <span class="keyword">uint32_t</span> *setmark,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> struct ovs_key_ct_labels *setlabel,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> struct <span class="keyword">nat_action_info_t</span> *nat_action_info)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">conn</span> *<span class="title">conn</span>;</span></span><br><span class="line">    <span class="keyword">unsigned</span> bucket = hash_to_bucket(ctx-&gt;hash);</span><br><span class="line">    ct_lock_lock(&amp;ct-&gt;buckets[bucket].lock);</span><br><span class="line">    conn_key_lookup(&amp;ct-&gt;buckets[bucket], ctx, now);</span><br><span class="line">    conn = ctx-&gt;conn;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">conn</span> <span class="title">conn_for_un_nat_copy</span>;</span></span><br><span class="line">    <span class="built_in">memset</span>(&amp;conn_for_un_nat_copy, <span class="number">0</span>, <span class="keyword">sizeof</span> conn_for_un_nat_copy);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//这块的代码类似函数conn_key_extract</span></span><br><span class="line">    <span class="comment">//然后执行的conn_key_lookup是取自上一级函数conntrack_execute</span></span><br><span class="line">    <span class="keyword">if</span> (OVS_LIKELY(conn)) &#123;</span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;conn_type == CT_CONN_TYPE_UN_NAT) &#123;</span><br><span class="line">            ctx-&gt;reply = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">conn_lookup_ctx</span> <span class="title">ctx2</span>;</span></span><br><span class="line">            ctx2.conn = <span class="literal">NULL</span>;</span><br><span class="line">            ctx2.key = conn-&gt;rev_key;</span><br><span class="line">            ctx2.hash = conn_key_hash(&amp;conn-&gt;rev_key, ct-&gt;hash_basis);</span><br><span class="line"></span><br><span class="line">            ct_lock_unlock(&amp;ct-&gt;buckets[bucket].lock);</span><br><span class="line">            bucket = hash_to_bucket(ctx2.hash);</span><br><span class="line"></span><br><span class="line">            ct_lock_lock(&amp;ct-&gt;buckets[bucket].lock);</span><br><span class="line">            conn_key_lookup(&amp;ct-&gt;buckets[bucket], &amp;ctx2, now);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (ctx2.conn) &#123;</span><br><span class="line">                conn = ctx2.conn;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">/* It is a race condition where conn has timed out and removed</span></span><br><span class="line"><span class="comment">                 * between unlock of the rev_conn and lock of the forward conn;</span></span><br><span class="line"><span class="comment">                 * nothing to do. */</span></span><br><span class="line">                ct_lock_unlock(&amp;ct-&gt;buckets[bucket].lock);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">bool</span> create_new_conn = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">if</span> (OVS_LIKELY(conn)) &#123;</span><br><span class="line">        <span class="comment">//此处更新conntrack信息，是原来的操作</span></span><br><span class="line">        create_new_conn = conn_update_state(ct, pkt, ctx, &amp;conn, now, bucket);</span><br><span class="line">        <span class="comment">//这里的操作NAT的函数是新的接口，主要是对报文的NAT操作，需要重点关注</span></span><br><span class="line">        <span class="keyword">if</span> (nat_action_info &amp;&amp; !create_new_conn) &#123;</span><br><span class="line">            handle_nat(pkt, conn, zone, ctx-&gt;reply);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (ctx-&gt;related) &#123;</span><br><span class="line">            pkt-&gt;md.ct_state = CS_INVALID;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            create_new_conn = <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (OVS_UNLIKELY(create_new_conn)) &#123;</span><br><span class="line">        <span class="comment">//连接没有创建之前，上面的操作都没有，先是执行这里创建连接。</span></span><br><span class="line">        <span class="comment">//该函数是之前就有的，用于创建新的连接的，但是现在增加了NAT参数，需要关注一下。</span></span><br><span class="line">        conn = conn_not_found(ct, pkt, ctx, commit, now, nat_action_info,</span><br><span class="line">                    &amp;conn_for_un_nat_copy);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    write_ct_md(pkt, zone, conn ? conn-&gt;mark : <span class="number">0</span>,</span><br><span class="line">                conn ? conn-&gt;label : OVS_U128_ZERO);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//以下的两个操作也是取自上一级函数conntrack_execute</span></span><br><span class="line">    <span class="keyword">if</span> (conn &amp;&amp; setmark) &#123;</span><br><span class="line">        set_mark(pkt, conn, setmark[<span class="number">0</span>], setmark[<span class="number">1</span>]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (conn &amp;&amp; setlabel) &#123;</span><br><span class="line">        set_label(pkt, conn, &amp;setlabel[<span class="number">0</span>], &amp;setlabel[<span class="number">1</span>]);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ct_lock_unlock(&amp;ct-&gt;buckets[bucket].lock);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//此处也是新添加的操作，主要是创建反向NAT连接表，需要重点关注</span></span><br><span class="line">    <span class="keyword">if</span> (conn_for_un_nat_copy.conn_type == CT_CONN_TYPE_UN_NAT) &#123;</span><br><span class="line">        create_un_nat_conn(ct, &amp;conn_for_un_nat_copy, now);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>首先看最简单的<code>conn_not_found</code>，创建连接表</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> struct conn *<span class="title">conn_not_found</span><span class="params">(struct conntrack *ct,</span></span></span><br><span class="line"><span class="function"><span class="params">            struct dp_packet *pkt, struct conn_lookup_ctx *ctx,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">bool</span> commit, <span class="keyword">long</span> <span class="keyword">long</span> now,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">const</span> struct <span class="keyword">nat_action_info_t</span> *nat_action_info,</span></span></span><br><span class="line"><span class="function"><span class="params">            struct conn *conn_for_un_nat_copy)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">unsigned</span> bucket = hash_to_bucket(ctx-&gt;hash);</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">conn</span> *<span class="title">nc</span> = <span class="title">NULL</span>;</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (!valid_new(pkt, &amp;ctx-&gt;key)) &#123;</span><br><span class="line">        pkt-&gt;md.ct_state = CS_INVALID;</span><br><span class="line">        <span class="keyword">return</span> nc;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//更新conntrack状态为new</span></span><br><span class="line">    pkt-&gt;md.ct_state = CS_NEW;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (commit) &#123;</span><br><span class="line">        <span class="keyword">unsigned</span> <span class="keyword">int</span> n_conn_limit;</span><br><span class="line"></span><br><span class="line">        atomic_read_relaxed(&amp;ct-&gt;n_conn_limit, &amp;n_conn_limit);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (atomic_count_get(&amp;ct-&gt;n_conn) &gt;= n_conn_limit) &#123;</span><br><span class="line">            COVERAGE_INC(conntrack_full);</span><br><span class="line">            <span class="keyword">return</span> nc;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        nc = new_conn(&amp;ct-&gt;buckets[bucket], pkt, &amp;ctx-&gt;key, now);</span><br><span class="line">        ctx-&gt;conn = nc;</span><br><span class="line">        <span class="built_in">memcpy</span>(&amp;nc-&gt;rev_key, &amp;nc-&gt;key, <span class="keyword">sizeof</span> nc-&gt;rev_key);</span><br><span class="line">        conn_key_reverse(&amp;nc-&gt;rev_key);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//由此以上没有变动，主要是创建连接，不同的协议调用不同的接口</span></span><br><span class="line">        <span class="comment">//由此以下是新加的代码，主要是计算IP Port范围元组的hash值，感觉是用的HASH模式</span></span><br><span class="line">        <span class="comment">//设置conn_type，然后调用nat_packet进行NAT转换，这个后面会说</span></span><br><span class="line">        <span class="comment">//conntrack因为只记录了元组信息，所以正反向都在这个连接里面记录了key和rev_key就可以</span></span><br><span class="line">        <span class="comment">//而NAT需要做操作，所以正反向的而连接针对的操作不一样，可以放在一个连接里面，</span></span><br><span class="line">        <span class="comment">//也可以放在两个连接里面，而作者选择了放在两个连接里面，这里只是给NAT申请了正向连接</span></span><br><span class="line">        <span class="comment">//反向连接是另外一个函数create_un_nat_conn做的</span></span><br><span class="line">        <span class="keyword">if</span> (nat_action_info) &#123;</span><br><span class="line">            nc-&gt;nat_info = xzalloc(<span class="keyword">sizeof</span> *nat_action_info);</span><br><span class="line">            <span class="built_in">memcpy</span>(nc-&gt;nat_info, nat_action_info, <span class="keyword">sizeof</span> *nc-&gt;nat_info);</span><br><span class="line">            ct_rwlock_wrlock(&amp;ct-&gt;nat_resources_lock);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//此处比较重要，到底NAT采用的是random还是hash还是其他映射方式由这里决定</span></span><br><span class="line">            <span class="keyword">bool</span> nat_res = nat_select_range_tuple(ct, nc,</span><br><span class="line">                        conn_for_un_nat_copy);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (!nat_res) &#123;</span><br><span class="line">                <span class="built_in">free</span>(nc-&gt;nat_info);</span><br><span class="line">                nc-&gt;nat_info = <span class="literal">NULL</span>;</span><br><span class="line">                <span class="built_in">free</span> (nc);</span><br><span class="line">                ct_rwlock_unlock(&amp;ct-&gt;nat_resources_lock);</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">NULL</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (conn_for_un_nat_copy &amp;&amp;</span><br><span class="line">                        nc-&gt;conn_type == CT_CONN_TYPE_DEFAULT) &#123;</span><br><span class="line">                *nc = *conn_for_un_nat_copy;</span><br><span class="line">                conn_for_un_nat_copy-&gt;conn_type = CT_CONN_TYPE_UN_NAT;</span><br><span class="line">            &#125;</span><br><span class="line">            ct_rwlock_unlock(&amp;ct-&gt;nat_resources_lock);</span><br><span class="line"></span><br><span class="line">            <span class="comment">//这个操作就是根据connection的规则对报文的IP Port进行修改</span></span><br><span class="line">            nat_packet(pkt, nc);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//由此以下没有变动，主要是添加connection信息到hash表中</span></span><br><span class="line">        hmap_insert(&amp;ct-&gt;buckets[bucket].connections, &amp;nc-&gt;node, ctx-&gt;hash);</span><br><span class="line">        atomic_count_inc(&amp;ct-&gt;n_conn);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> nc;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>nat_select_range_tuple</code>主要是在nat连接创建的时候，确定连接需要修改的IP和Port</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">nat_select_range_tuple</span><span class="params">(struct conntrack *ct, <span class="keyword">const</span> struct conn *conn,</span></span></span><br><span class="line"><span class="function"><span class="params">            struct conn *nat_conn)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MIN_NAT_EPHEMERAL_PORT 1024</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> MAX_NAT_EPHEMERAL_PORT 65535</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint16_t</span> min_port;</span><br><span class="line">    <span class="keyword">uint16_t</span> max_port;</span><br><span class="line">    <span class="keyword">uint16_t</span> first_port;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//根据几元组计算hash，主要是最小最大IP Port和3、4层协议类型以及zone</span></span><br><span class="line">    <span class="keyword">uint32_t</span> hash = nat_range_hash(conn, ct-&gt;hash_basis);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> ((conn-&gt;nat_info-&gt;nat_action &amp; NAT_ACTION_SRC) &amp;&amp;</span><br><span class="line">                (!(conn-&gt;nat_info-&gt;nat_action &amp; NAT_ACTION_SRC_PORT))) &#123;</span><br><span class="line">        <span class="comment">//当只设置了IP SNAT的时候，port固定</span></span><br><span class="line">        min_port = ntohs(conn-&gt;key.src.port);</span><br><span class="line">        max_port = ntohs(conn-&gt;key.src.port);</span><br><span class="line">        first_port = min_port;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> ((conn-&gt;nat_info-&gt;nat_action &amp; NAT_ACTION_DST) &amp;&amp;</span><br><span class="line">                (!(conn-&gt;nat_info-&gt;nat_action &amp; NAT_ACTION_DST_PORT))) &#123;</span><br><span class="line">        <span class="comment">//当只设置了IP DNAT的时候，port固定</span></span><br><span class="line">        min_port = ntohs(conn-&gt;key.dst.port);</span><br><span class="line">        max_port = ntohs(conn-&gt;key.dst.port);</span><br><span class="line">        first_port = min_port;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">//根据hash取出来一个Port作为第一个Port</span></span><br><span class="line">        <span class="keyword">uint16_t</span> deltap = conn-&gt;nat_info-&gt;max_port - conn-&gt;nat_info-&gt;min_port;</span><br><span class="line">        <span class="keyword">uint32_t</span> port_index = hash % (deltap + <span class="number">1</span>);</span><br><span class="line">        first_port = conn-&gt;nat_info-&gt;min_port + port_index;</span><br><span class="line">        min_port = conn-&gt;nat_info-&gt;min_port;</span><br><span class="line">        max_port = conn-&gt;nat_info-&gt;max_port;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint32_t</span> deltaa = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">uint32_t</span> address_index;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">ct_addr</span> <span class="title">ct_addr</span>;</span></span><br><span class="line">    <span class="built_in">memset</span>(&amp;ct_addr, <span class="number">0</span>, <span class="keyword">sizeof</span> ct_addr);</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">ct_addr</span> <span class="title">max_ct_addr</span>;</span></span><br><span class="line">    <span class="built_in">memset</span>(&amp;max_ct_addr, <span class="number">0</span>, <span class="keyword">sizeof</span> max_ct_addr);</span><br><span class="line">    max_ct_addr = conn-&gt;nat_info-&gt;max_addr;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IP)) &#123;</span><br><span class="line">        deltaa = ntohl(conn-&gt;nat_info-&gt;max_addr.ipv4_aligned) -</span><br><span class="line">            ntohl(conn-&gt;nat_info-&gt;min_addr.ipv4_aligned);</span><br><span class="line">        address_index = hash % (deltaa + <span class="number">1</span>);</span><br><span class="line">        <span class="comment">//根据hash计算出一个IP作为第一个IP</span></span><br><span class="line">        ct_addr.ipv4_aligned = htonl(</span><br><span class="line">                    ntohl(conn-&gt;nat_info-&gt;min_addr.ipv4_aligned) + address_index);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        deltaa = nat_ipv6_addrs_delta(&amp;conn-&gt;nat_info-&gt;min_addr.ipv6_aligned,</span><br><span class="line">                    &amp;conn-&gt;nat_info-&gt;max_addr.ipv6_aligned);</span><br><span class="line">        <span class="comment">/* deltaa must be within 32 bits for full hash coverage. A 64 or</span></span><br><span class="line"><span class="comment">         * 128 bit hash is unnecessary and hence not used here. Most code</span></span><br><span class="line"><span class="comment">         * is kept common with V4; nat_ipv6_addrs_delta() will do the</span></span><br><span class="line"><span class="comment">         * enforcement via max_ct_addr. */</span></span><br><span class="line">        max_ct_addr = conn-&gt;nat_info-&gt;min_addr;</span><br><span class="line">        nat_ipv6_addr_increment(&amp;max_ct_addr.ipv6_aligned, deltaa);</span><br><span class="line"></span><br><span class="line">        address_index = hash % (deltaa + <span class="number">1</span>);</span><br><span class="line">        ct_addr.ipv6_aligned = conn-&gt;nat_info-&gt;min_addr.ipv6_aligned;</span><br><span class="line">        nat_ipv6_addr_increment(&amp;ct_addr.ipv6_aligned, address_index);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">uint16_t</span> port = first_port;</span><br><span class="line">    <span class="keyword">bool</span> all_ports_tried = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">bool</span> original_ports_tried = <span class="literal">false</span>;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">ct_addr</span> <span class="title">first_addr</span> = <span class="title">ct_addr</span>;</span></span><br><span class="line">    *nat_conn = *conn;</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="comment">//设置rev_key值需要修改的IP和Port，在nat_packet中使用</span></span><br><span class="line">        <span class="comment">//通过循环，会根据IP和Port的范围完成每一项的设置</span></span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;nat_info-&gt;nat_action &amp; NAT_ACTION_SRC) &#123;</span><br><span class="line">            nat_conn-&gt;rev_key.dst.addr = ct_addr;</span><br><span class="line">            nat_conn-&gt;rev_key.dst.port = htons(port);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            nat_conn-&gt;rev_key.src.addr = ct_addr;</span><br><span class="line">            nat_conn-&gt;rev_key.src.port = htons(port);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="class"><span class="keyword">struct</span> <span class="title">nat_conn_key_node</span> *<span class="title">nat_conn_key_node</span> =</span></span><br><span class="line"><span class="class">            <span class="title">nat_conn_keys_lookup</span>(&amp;<span class="title">ct</span>-&gt;<span class="title">nat_conn_keys</span>, &amp;<span class="title">nat_conn</span>-&gt;<span class="title">rev_key</span>,</span></span><br><span class="line"><span class="class">                        <span class="title">ct</span>-&gt;<span class="title">hash_basis</span>);</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!nat_conn_key_node) &#123;</span><br><span class="line">            <span class="comment">//未找到时创建，说明没有该IP和端口未使用，直接返回</span></span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">nat_conn_key_node</span> *<span class="title">nat_conn_key</span> =</span></span><br><span class="line"><span class="class">                <span class="title">xzalloc</span>(<span class="title">sizeof</span> *<span class="title">nat_conn_key</span>);</span></span><br><span class="line">            <span class="built_in">memcpy</span>(&amp;nat_conn_key-&gt;key, &amp;nat_conn-&gt;rev_key,</span><br><span class="line">                        <span class="keyword">sizeof</span> nat_conn_key-&gt;key);</span><br><span class="line">            <span class="built_in">memcpy</span>(&amp;nat_conn_key-&gt;value, &amp;nat_conn-&gt;key,</span><br><span class="line">                        <span class="keyword">sizeof</span> nat_conn_key-&gt;value);</span><br><span class="line">            <span class="keyword">uint32_t</span> nat_conn_key_hash = conn_key_hash(&amp;nat_conn_key-&gt;key,</span><br><span class="line">                        ct-&gt;hash_basis);</span><br><span class="line">            hmap_insert(&amp;ct-&gt;nat_conn_keys, &amp;nat_conn_key-&gt;node,</span><br><span class="line">                        nat_conn_key_hash);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (!all_ports_tried) &#123;</span><br><span class="line">            <span class="comment">//未遍历完所有的Port时调用</span></span><br><span class="line">            <span class="comment">//Port一直自加1，知道最大之后，回到最小</span></span><br><span class="line">            <span class="comment">//直到碰到被设置的第一个Port，遍历完成</span></span><br><span class="line">            <span class="keyword">if</span> (min_port == max_port) &#123;</span><br><span class="line">                all_ports_tried = <span class="literal">true</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (port == max_port) &#123;</span><br><span class="line">                port = min_port;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                port++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (port == first_port) &#123;</span><br><span class="line">                all_ports_tried = <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//前面都遍历完端口时会调用</span></span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">memcmp</span>(&amp;ct_addr, &amp;max_ct_addr, <span class="keyword">sizeof</span> ct_addr)) &#123;</span><br><span class="line">                <span class="comment">//如果当前的IP不是最大IP，则进行++的操作</span></span><br><span class="line">                <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IP)) &#123;</span><br><span class="line">                    ct_addr.ipv4_aligned = htonl(</span><br><span class="line">                                ntohl(ct_addr.ipv4_aligned) + <span class="number">1</span>);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    nat_ipv6_addr_increment(&amp;ct_addr.ipv6_aligned, <span class="number">1</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">//如果已经到了最大IP，则回到最小的IP</span></span><br><span class="line">                ct_addr = conn-&gt;nat_info-&gt;min_addr;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (!<span class="built_in">memcmp</span>(&amp;ct_addr, &amp;first_addr, <span class="keyword">sizeof</span> ct_addr)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!original_ports_tried) &#123;</span><br><span class="line">                    original_ports_tried = <span class="literal">true</span>;</span><br><span class="line">                    ct_addr = conn-&gt;nat_info-&gt;min_addr;</span><br><span class="line">                    min_port = MIN_NAT_EPHEMERAL_PORT;</span><br><span class="line">                    max_port = MAX_NAT_EPHEMERAL_PORT;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            first_port = min_port;</span><br><span class="line">            port = first_port;</span><br><span class="line">            all_ports_tried = <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来看一下接口<code>create_un_nat_conn</code>，这个函数主要是创建反向NAT连接的数据结构</p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">create_un_nat_conn</span><span class="params">(struct conntrack *ct,</span></span></span><br><span class="line"><span class="function"><span class="params">            struct conn *conn_for_un_nat_copy, <span class="keyword">long</span> <span class="keyword">long</span> now)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">conn</span> *<span class="title">nc</span> = <span class="title">xzalloc</span>(<span class="title">sizeof</span> *<span class="title">nc</span>);</span></span><br><span class="line">    <span class="built_in">memcpy</span>(nc, conn_for_un_nat_copy, <span class="keyword">sizeof</span> *nc);</span><br><span class="line">    <span class="comment">//获取正向的connection的key值</span></span><br><span class="line">    nc-&gt;key = conn_for_un_nat_copy-&gt;rev_key;</span><br><span class="line">    nc-&gt;rev_key = conn_for_un_nat_copy-&gt;key;</span><br><span class="line">    <span class="keyword">uint32_t</span> un_nat_hash = conn_key_hash(&amp;nc-&gt;key, ct-&gt;hash_basis);</span><br><span class="line">    <span class="keyword">unsigned</span> un_nat_conn_bucket = hash_to_bucket(un_nat_hash);</span><br><span class="line">    ct_lock_lock(&amp;ct-&gt;buckets[un_nat_conn_bucket].lock);</span><br><span class="line">    ct_rwlock_rdlock(&amp;ct-&gt;nat_resources_lock);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//这里首先查一下需要创建的这个连接的反向连接的信息，即正向连接的信息</span></span><br><span class="line">    <span class="comment">//确保有正向连接，才接下来创建这个反向连接，和正向连接的创建类似</span></span><br><span class="line">    <span class="comment">//这里也是新添加的一个函数，可以看一下</span></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">conn</span> *<span class="title">rev_conn</span> = <span class="title">conn_lookup</span>(<span class="title">ct</span>, &amp;<span class="title">nc</span>-&gt;<span class="title">key</span>, <span class="title">now</span>);</span></span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">nat_conn_key_node</span> *<span class="title">nat_conn_key_node</span> =</span></span><br><span class="line"><span class="class">        <span class="title">nat_conn_keys_lookup</span>(&amp;<span class="title">ct</span>-&gt;<span class="title">nat_conn_keys</span>, &amp;<span class="title">nc</span>-&gt;<span class="title">key</span>, <span class="title">ct</span>-&gt;<span class="title">hash_basis</span>);</span></span><br><span class="line">    <span class="keyword">if</span> (nat_conn_key_node &amp;&amp; !<span class="built_in">memcmp</span>(&amp;nat_conn_key_node-&gt;value,</span><br><span class="line">                    &amp;nc-&gt;rev_key, <span class="keyword">sizeof</span> nat_conn_key_node-&gt;value) &amp;&amp; !rev_conn) &#123;</span><br><span class="line"></span><br><span class="line">        hmap_insert(&amp;ct-&gt;buckets[un_nat_conn_bucket].connections,</span><br><span class="line">                    &amp;nc-&gt;node, un_nat_hash);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="built_in">free</span>(nc);</span><br><span class="line">    &#125;</span><br><span class="line">    ct_rwlock_unlock(&amp;ct-&gt;nat_resources_lock);</span><br><span class="line">    ct_lock_unlock(&amp;ct-&gt;buckets[un_nat_conn_bucket].lock);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后看一下函数<code>handle_nat</code></p><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">handle_nat</span><span class="params">(struct dp_packet *pkt, struct conn *conn,</span></span></span><br><span class="line"><span class="function"><span class="params">            <span class="keyword">uint16_t</span> zone, <span class="keyword">bool</span> reply)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//对ct_state的限定条件没看懂</span></span><br><span class="line">    <span class="keyword">if</span> ((conn-&gt;nat_info) &amp;&amp;</span><br><span class="line">                (!(pkt-&gt;md.ct_state &amp; (CS_SRC_NAT | CS_DST_NAT)) ||</span><br><span class="line">                 (pkt-&gt;md.ct_state &amp; (CS_SRC_NAT | CS_DST_NAT) &amp;&amp;</span><br><span class="line">                  zone != pkt-&gt;md.ct_zone)))&#123;</span><br><span class="line">        <span class="keyword">if</span> (pkt-&gt;md.ct_state &amp; (CS_SRC_NAT | CS_DST_NAT)) &#123;</span><br><span class="line">            pkt-&gt;md.ct_state &amp;= ~(CS_SRC_NAT | CS_DST_NAT);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//一下就是正向和反向流量的NAT操作，</span></span><br><span class="line">        <span class="comment">//主要是修改报文头的IP或者Port，重新计算校验</span></span><br><span class="line">        <span class="keyword">if</span> (reply) &#123;</span><br><span class="line">            un_nat_packet(pkt, conn);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            nat_packet(pkt, conn);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">nat_packet</span><span class="params">(struct dp_packet *pkt, <span class="keyword">const</span> struct conn *conn)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//代码没怎么复用，所以有点多，粗略看一下</span></span><br><span class="line">    <span class="keyword">if</span> (conn-&gt;nat_info-&gt;nat_action &amp; NAT_ACTION_SRC) &#123;</span><br><span class="line">        pkt-&gt;md.ct_state |= CS_SRC_NAT;</span><br><span class="line">        <span class="comment">//修改源IP，这里面会重新计算校验和</span></span><br><span class="line">        <span class="comment">//下面也都会重新计算，所以一次改IP和Port的时候会计算两次</span></span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IP)) &#123;</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">ip_header</span> *<span class="title">nh</span> = <span class="title">dp_packet_l3</span>(<span class="title">pkt</span>);</span></span><br><span class="line">            packet_set_ipv4_addr(pkt, &amp;nh-&gt;ip_src,</span><br><span class="line">                        conn-&gt;rev_key.dst.addr.ipv4_aligned);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IPV6)) &#123;</span><br><span class="line">            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">in6_addr</span> <span class="title">ipv6_addr</span>;</span></span><br><span class="line">            <span class="built_in">memcpy</span>(&amp;ipv6_addr, conn-&gt;rev_key.dst.addr.ipv6.be32,</span><br><span class="line">                        <span class="keyword">sizeof</span> ipv6_addr);</span><br><span class="line">            packet_set_ipv6_addr(pkt, conn-&gt;key.nw_proto,</span><br><span class="line">                        nh6-&gt;ip6_src.be32, &amp;ipv6_addr, <span class="literal">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//修改源Port</span></span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;key.nw_proto == IPPROTO_TCP) &#123;</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">tcp_header</span> *<span class="title">th</span> = <span class="title">dp_packet_l4</span>(<span class="title">pkt</span>);</span></span><br><span class="line">            packet_set_tcp_port(pkt, conn-&gt;rev_key.dst.port, th-&gt;tcp_dst);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;key.nw_proto == IPPROTO_UDP) &#123;</span><br><span class="line">            struct udp_header *uh = dp_packet_l4(pkt);</span><br><span class="line">            packet_set_udp_port(pkt, conn-&gt;rev_key.dst.port, uh-&gt;udp_dst);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;nat_info-&gt;nat_action &amp; NAT_ACTION_DST) &#123;</span><br><span class="line">        pkt-&gt;md.ct_state |= CS_DST_NAT;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//修改目的IP</span></span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IP)) &#123;</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">ip_header</span> *<span class="title">nh</span> = <span class="title">dp_packet_l3</span>(<span class="title">pkt</span>);</span></span><br><span class="line">            packet_set_ipv4_addr(pkt, &amp;nh-&gt;ip_dst,</span><br><span class="line">                        conn-&gt;rev_key.src.addr.ipv4_aligned);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IPV6)) &#123;</span><br><span class="line">            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);</span><br><span class="line"></span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">in6_addr</span> <span class="title">ipv6_addr</span>;</span></span><br><span class="line">            <span class="built_in">memcpy</span>(&amp;ipv6_addr, conn-&gt;rev_key.dst.addr.ipv6.be32,</span><br><span class="line">                        <span class="keyword">sizeof</span> ipv6_addr);</span><br><span class="line">            packet_set_ipv6_addr(pkt, conn-&gt;key.nw_proto,</span><br><span class="line">                        nh6-&gt;ip6_dst.be32, &amp;ipv6_addr, <span class="literal">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//修改目的Port</span></span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;key.nw_proto == IPPROTO_TCP) &#123;</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">tcp_header</span> *<span class="title">th</span> = <span class="title">dp_packet_l4</span>(<span class="title">pkt</span>);</span></span><br><span class="line">            packet_set_tcp_port(pkt, th-&gt;tcp_src, conn-&gt;rev_key.src.port);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;key.nw_proto == IPPROTO_UDP) &#123;</span><br><span class="line">            struct udp_header *uh = dp_packet_l4(pkt);</span><br><span class="line">            packet_set_udp_port(pkt, uh-&gt;udp_src, conn-&gt;rev_key.src.port);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">void</span></span><br><span class="line">un_nat_packet(struct dp_packet *pkt, <span class="keyword">const</span> struct conn *conn)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">//跟前面差不多，这个是反向流的修改</span></span><br><span class="line">    <span class="comment">//只需要注意这边的SRC和DST是要互换一下的</span></span><br><span class="line">    <span class="keyword">if</span> (conn-&gt;nat_info-&gt;nat_action &amp; NAT_ACTION_SRC) &#123;</span><br><span class="line">        pkt-&gt;md.ct_state |= CS_SRC_NAT;</span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IP)) &#123;</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">ip_header</span> *<span class="title">nh</span> = <span class="title">dp_packet_l3</span>(<span class="title">pkt</span>);</span></span><br><span class="line">            packet_set_ipv4_addr(pkt, &amp;nh-&gt;ip_dst,</span><br><span class="line">                        conn-&gt;key.src.addr.ipv4_aligned);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IPV6)) &#123;</span><br><span class="line">            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">in6_addr</span> <span class="title">ipv6_addr</span>;</span></span><br><span class="line">            <span class="built_in">memcpy</span>(&amp;ipv6_addr, conn-&gt;key.src.addr.ipv6.be32,</span><br><span class="line">                        <span class="keyword">sizeof</span> ipv6_addr);</span><br><span class="line">            packet_set_ipv6_addr(pkt, conn-&gt;key.nw_proto,</span><br><span class="line">                        nh6-&gt;ip6_dst.be32, &amp;ipv6_addr, <span class="literal">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;key.nw_proto == IPPROTO_TCP) &#123;</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">tcp_header</span> *<span class="title">th</span> = <span class="title">dp_packet_l4</span>(<span class="title">pkt</span>);</span></span><br><span class="line">            packet_set_tcp_port(pkt, th-&gt;tcp_src, conn-&gt;key.src.port);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;key.nw_proto == IPPROTO_UDP) &#123;</span><br><span class="line">            struct udp_header *uh = dp_packet_l4(pkt);</span><br><span class="line">            packet_set_udp_port(pkt, uh-&gt;udp_src, conn-&gt;key.src.port);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;nat_info-&gt;nat_action &amp; NAT_ACTION_DST) &#123;</span><br><span class="line">        pkt-&gt;md.ct_state |= CS_DST_NAT;</span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IP)) &#123;</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">ip_header</span> *<span class="title">nh</span> = <span class="title">dp_packet_l3</span>(<span class="title">pkt</span>);</span></span><br><span class="line">            packet_set_ipv4_addr(pkt, &amp;nh-&gt;ip_src,</span><br><span class="line">                        conn-&gt;key.dst.addr.ipv4_aligned);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;key.dl_type == htons(ETH_TYPE_IPV6)) &#123;</span><br><span class="line">            struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">in6_addr</span> <span class="title">ipv6_addr</span>;</span></span><br><span class="line">            <span class="built_in">memcpy</span>(&amp;ipv6_addr, conn-&gt;key.dst.addr.ipv6.be32,</span><br><span class="line">                        <span class="keyword">sizeof</span> ipv6_addr);</span><br><span class="line">            packet_set_ipv6_addr(pkt, conn-&gt;key.nw_proto,</span><br><span class="line">                        nh6-&gt;ip6_src.be32, &amp;ipv6_addr, <span class="literal">true</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (conn-&gt;key.nw_proto == IPPROTO_TCP) &#123;</span><br><span class="line">            <span class="class"><span class="keyword">struct</span> <span class="title">tcp_header</span> *<span class="title">th</span> = <span class="title">dp_packet_l4</span>(<span class="title">pkt</span>);</span></span><br><span class="line">            packet_set_tcp_port(pkt, conn-&gt;key.dst.port, th-&gt;tcp_dst);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (conn-&gt;key.nw_proto == IPPROTO_UDP) &#123;</span><br><span class="line">            struct udp_header *uh = dp_packet_l4(pkt);</span><br><span class="line">            packet_set_udp_port(pkt, conn-&gt;key.dst.port, uh-&gt;udp_dst);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>除去上面说的接口，还有其他一些地方的修改，这里简单的列一下</p><ul><li><code>conntrack_init</code>添加了创建记录NAT connection key值的hash表</li><li><code>conntrack_destroy</code>删除上述hash表</li><li><code>nat_destroy</code>主要是销毁指定的NAT配置，主要是删除上面的key的hash表，connection的hash表以及反向的key的hash表和connection的hash表</li><li><code>conn_update_state</code>函数是更新conntrack的状态，原版的是在<code>process_one</code>实现的，patch单独提出来了，添加了删除connection的时候一起删除nat的操作<code>nat_destroy</code></li><li><code>sweep_bucket</code>主要是清楚超时的conntrack，也是添加了删除nat的操作<code>nat_destroy</code></li><li><code>nat_select_range_tuple</code>调用的一系列的接口我们不关注了<ul><li><code>nat_ipv6_addrs_delta</code></li><li><code>nat_ipv6_addr_increment</code></li><li><code>nat_range_hash</code></li></ul></li><li><code>nat_conn_keys_lookup</code>比较简单，就是hash表中查找</li><li><code>nat_conn_keys_remove</code>就是hash表删除</li><li><code>conntrack_flush</code>主要是清空conntrack，这里添加了清空NAT的操作</li></ul></div><div></div><footer class="post-footer"><div class="post-nav"><div class="post-nav-next post-nav-item"><a href="/2017/02/06/SDN/OVS/2017-02-06-qos/" rel="next" title="OpenvSwitch Qos"><i class="fa fa-chevron-left"></i> OpenvSwitch Qos</a></div><div class="post-nav-prev post-nav-item"> <a href="/2017/02/14/VPP/2017-02-14-VPP-BiHash/" rel="prev" title="[转]Cisco VPP BiHash分析">[转]Cisco VPP BiHash分析<i class="fa fa-chevron-right"></i></a></div></div></footer></div></article><div class="post-copyright"><p><span>注意:</span>所有文章非特别说明皆为原创。为保证信息与源同步，转载时请务必注明文章出处！谢谢合作 :-)</p><p> <span>原始链接:</span><a class="post-url" href="/2017/02/10/SDN/OVS/2017-02-10-conntrack/" title="OpenvSwitch Conntrack & NAT">http://zhaozhanxu.com/2017/02/10/SDN/OVS/2017-02-10-conntrack/</a><span class="copy-path" data-clipboard-text="原文: http://zhaozhanxu.com/2017/02/10/SDN/OVS/2017-02-10-conntrack/　　作者: 赵占旭" title="点击复制文章链接"><i class="fa fa-clipboard"></i></span><script src="/js/clipboard.min.js"></script><script>var clipboard=new Clipboard(".copy-path")</script></p><p> <span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/cn" title="中国大陆 (CC BY-NC-SA 4.0 CN)" target="_blank">"署名-非商用-相同方式共享 4.0"</a> 转载请保留原文链接及作者。</p></div></div></div><div class="comments" id="comments"></div></div><div class="sidebar-toggle"><div class="sidebar-toggle-line-wrap"><span class="sidebar-toggle-line sidebar-toggle-line-first"></span><span class="sidebar-toggle-line sidebar-toggle-line-middle"></span><span class="sidebar-toggle-line sidebar-toggle-line-last"></span></div></div><aside id="sidebar" class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap"> Table of Contents</li><li class="sidebar-nav-overview" data-target="site-overview"> Overview</li></ul><section class="site-overview sidebar-panel"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a href="/about"><img class="site-author-image" itemprop="image" src="/images/public/me.jpg" alt="赵占旭"><p class="site-author-name" itemprop="name">赵占旭</p><p class="site-description motion-element" itemprop="description"></p></a></div><nav class="site-state motion-element"><div class="site-state-item site-state-posts"> <a href="/archives"><span class="site-state-item-count">61</span> <span class="site-state-item-name">posts</span></a></div><div class="site-state-item site-state-categories"> <a href="/categories"><span class="site-state-item-count">7</span> <span class="site-state-item-name">categories</span></a></div><div class="site-state-item site-state-tags"> <a href="/tags"><span class="site-state-item-count">14</span> <span class="site-state-item-name">tags</span></a></div></nav><div class="feed-link motion-element"><a href="/atom.xml" rel="alternate"><i class="fa fa-rss"></i> RSS</a></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="mailto:zhaozhanxu@163.com" target="_blank" title="Mail"><i class="fa fa-fw fa-envelope"></i> Mail</a></span><span class="links-of-author-item"><a href="https://github.com/zhaozhanxu" target="_blank" title="GitHub"><i class="fa fa-fw fa-github"></i> GitHub</a></span><span class="links-of-author-item"><a href="https://www.linkedin.com/in/zhaozhanxu" target="_blank" title="LinkedIn"><i class="fa fa-fw fa-linkedin"></i> LinkedIn</a></span><span class="links-of-author-item"><a href="https://twitter.com/zhaozhanxu" target="_blank" title="Twitter"><i class="fa fa-fw fa-twitter"></i> Twitter</a></span><span class="links-of-author-item"><a href="/about/" target="_blank" title="Wechat"><i class="fa fa-fw fa-wechat"></i> Wechat</a></span><span class="links-of-author-item"><a href="/about/" target="_blank" title="QQ"><i class="fa fa-fw fa-qq"></i> QQ</a></span></div><div class="links-of-blogroll motion-element links-of-blogroll-inline"><div class="links-of-blogroll-title"><i class="fa fa-fw fa-address-book"></i> 基情链接</div><ul class="links-of-blogroll-list"><li class="links-of-blogroll-item"> <a href="http://blog.csdn.net/jqh9804" title="呆子辉" target="_blank">呆子辉</a></li></ul></div></section><section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active"><div class="post-toc"><div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#conntrack匹配项说明"><span class="nav-number">1.</span><span class="nav-text"><a href="#conntrack&#x5339;&#x914D;&#x9879;&#x8BF4;&#x660E;" class="headerlink" title="conntrack&#x5339;&#x914D;&#x9879;&#x8BF4;&#x660E;"></a> conntrack&#x5339;&#x914D;&#x9879;&#x8BF4;&#x660E;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#conntrack-actions执行"><span class="nav-number">2.</span><span class="nav-text"><a href="#conntrack-actions&#x6267;&#x884C;" class="headerlink" title="conntrack actions&#x6267;&#x884C;"></a> conntrack actions&#x6267;&#x884C;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#ACL示例"><span class="nav-number">3.</span><span class="nav-text"><a href="#ACL&#x793A;&#x4F8B;" class="headerlink" title="ACL&#x793A;&#x4F8B;"></a> ACL&#x793A;&#x4F8B;</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#应用层流表"><span class="nav-number">3.1.</span><span class="nav-text"><a href="#&#x5E94;&#x7528;&#x5C42;&#x6D41;&#x8868;" class="headerlink" title="&#x5E94;&#x7528;&#x5C42;&#x6D41;&#x8868;"></a> &#x5E94;&#x7528;&#x5C42;&#x6D41;&#x8868;</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#datapath流表"><span class="nav-number">3.2.</span><span class="nav-text"><a href="#datapath&#x6D41;&#x8868;" class="headerlink" title="datapath&#x6D41;&#x8868;"></a> datapath&#x6D41;&#x8868;</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#NAT示例"><span class="nav-number">4.</span><span class="nav-text"><a href="#NAT&#x793A;&#x4F8B;" class="headerlink" title="NAT&#x793A;&#x4F8B;"></a> NAT&#x793A;&#x4F8B;</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#应用层流表-1"><span class="nav-number">4.1.</span><span class="nav-text"><a href="#&#x5E94;&#x7528;&#x5C42;&#x6D41;&#x8868;-1" class="headerlink" title="&#x5E94;&#x7528;&#x5C42;&#x6D41;&#x8868;"></a> &#x5E94;&#x7528;&#x5C42;&#x6D41;&#x8868;</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#datapath流表-1"><span class="nav-number">4.2.</span><span class="nav-text"><a href="#datapath&#x6D41;&#x8868;-1" class="headerlink" title="datapath&#x6D41;&#x8868;"></a> datapath&#x6D41;&#x8868;</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#datapath代码分析"><span class="nav-number">5.</span><span class="nav-text"><a href="#datapath&#x4EE3;&#x7801;&#x5206;&#x6790;" class="headerlink" title="datapath&#x4EE3;&#x7801;&#x5206;&#x6790;"></a> datapath&#x4EE3;&#x7801;&#x5206;&#x6790;</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#NAT补丁"><span class="nav-number">6.</span><span class="nav-text"><a href="#NAT&#x8865;&#x4E01;" class="headerlink" title="NAT&#x8865;&#x4E01;"></a> NAT&#x8865;&#x4E01;</span></a></li></ol></div></div></section></div></aside></div></main><footer id="footer" class="footer"><div class="footer-inner"><div class="copyright"> &copy; 2016 - <span itemprop="copyrightYear">2017</span><span class="with-love"><i class="fa fa-heart"></i></span> <span class="author" itemprop="copyrightHolder">赵占旭</span></div><div class="powered-by"> Powered by <a class="theme-link" href="https://hexo.io">Hexo</a></div><div class="theme-info"> Theme - <a class="theme-link" href="https://github.com/iissnan/hexo-theme-next">NexT</a></div><div class="busuanzi-count"><script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script><span class="site-uv"><i class="fa fa-user"></i><span class="busuanzi-value" id="busuanzi_value_site_uv"></span></span><span class="site-pv"><i class="fa fa-eye"></i><span class="busuanzi-value" id="busuanzi_value_page_pv"></span></span></div></div></footer><div class="back-to-top"><i class="fa fa-arrow-up"></i></div></div><script type="text/javascript">"[object Function]"!==Object.prototype.toString.call(window.Promise)&&(window.Promise=null)</script><script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script><script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script><script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script><script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script><script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script><script type="text/javascript" src="/lib/canvas-nest/canvas-nest.min.js"></script><script type="text/javascript" src="/js/src/utils.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/motion.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/affix.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/post-details.js?v=5.1.0"></script><script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.0"></script><script type="text/javascript">function proceedsearch(){$("body").append('<div class="popoverlay">').css("overflow","hidden"),$(".popup").toggle()}var isfetched=!1,search_path="search.xml";0==search_path.length&&(search_path="search.xml");var path="/"+search_path,searchFunc=function(e,t,a){"use strict";$.ajax({url:e,dataType:"xml",async:!0,success:function(e){isfetched=!0,$(".popup").detach().appendTo(".header-inner");var r=$("entry",e).map(function(){return{title:$("title",this).text(),content:$("content",this).text(),url:$("url",this).text()}}).get(),c=document.getElementById(t),n=document.getElementById(a);c.addEventListener("input",function(){var e=0,t='<ul class="search-result-list">',a=this.value.trim().toLowerCase().split(/[\s\-]+/);n.innerHTML="",this.value.trim().length>1&&r.forEach(function(r){var c=!1,n=r.title.trim().toLowerCase(),s=r.content.trim().replace(/<[^>]+>/g,"").toLowerCase(),o=decodeURIComponent(r.url),i=-1,l=-1,p=-1;if(""!=n&&a.forEach(function(e,t){i=n.indexOf(e),l=s.indexOf(e),(i>=0||l>=0)&&(c=!0,0==t&&(p=l))}),c){e+=1,t+="<li><a href='"+o+"' class='search-result-title'>"+n+"</a>";var h=r.content.trim().replace(/<[^>]+>/g,"");if(p>=0){var u=p-20,d=p+80;u<0&&(u=0),0==u&&(d=50),d>h.length&&(d=h.length);var f=h.substring(u,d);a.forEach(function(e){var t=new RegExp(e,"gi");f=f.replace(t,'<b class="search-keyword">'+e+"</b>")}),t+='<p class="search-result">'+f+"...</p>"}t+="</li>"}}),t+="</ul>",0==e&&(t='<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'),""==a&&(t='<div id="no-result"><i class="fa fa-search fa-5x" /></div>'),n.innerHTML=t}),proceedsearch()}})};$(".popup-trigger").click(function(e){e.stopPropagation(),0==isfetched?searchFunc(path,"local-search-input","local-search-result"):proceedsearch()}),$(".popup-btn-close").click(function(e){$(".popup").hide(),$(".popoverlay").remove(),$("body").css("overflow","")}),$(".popup").click(function(e){e.stopPropagation()})</script><script>!function(){var t=document.createElement("script"),e=window.location.protocol.split(":")[0];t.src="https"===e?"https://zz.bdstatic.com/linksubmit/push.js":"http://push.zhanzhang.baidu.com/push.js";var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(t,s)}()</script></body></html>